# -*- coding: utf-8 -*-
# Form based authentication for CherryPy. Requires the
# Session tool to be loaded.
#

import sqlalchemy
import cherrypy
import user
from anketa import Anketa
import hashlib
import db
from datetime import date
import sqlalchemy.exc
from index import template

User = user.User

def check_credentials(username, password):
    """Verifies credentials for username and password.
    Returns None on success or a string describing the error on failure"""
    username = str(username)
    if not username:
        return "Не указано имя пользователя"
    u = db.session.query(User).get(username)
    if u is None:
        return "Имя пользователя {} отсутствует в базе данных.".format(username)
    if u.password != password:
         return "Неверный пароль. Попробуйте еще раз."


def check_auth(*args, **kwargs):
    """A tool that looks in config for 'auth.require'. If found and it
    is not None, a login is required and the entry is evaluated as a list of
    conditions that the user must fulfill"""
    conditions = cherrypy.request.config.get('auth.require', None)
    if conditions is not None:
        username = cherrypy.session.get(db.SESSION_KEY)
        if username:
            cherrypy.request.login = username
            for condition in conditions:
                # A condition is just a callable that returns true or false
                if not condition():
                    raise cherrypy.HTTPRedirect("/auth/login")
        else:
            raise cherrypy.HTTPRedirect("/auth/login")
    
def require(*conditions):
    """A decorator that appends conditions to the auth.require config
    variable."""
    def decorate(f):
        if not hasattr(f, '_cp_config'):
            f._cp_config = dict()
        if 'auth.require' not in f._cp_config:
            f._cp_config['auth.require'] = []
        f._cp_config['auth.require'].extend(conditions)
        return f
    return decorate

cherrypy.tools.auth = cherrypy.Tool('before_handler', check_auth)

# Conditions are callables that return True
# if the user fulfills the conditions they define, False otherwise
#
# They can access the current username as cherrypy.request.login
#
# Define those at will however suits the application.

def member_of(groupname):
    def check():
        if db.SESSION_KEY not in cherrypy.session.keys() or not cherrypy.session[db.SESSION_KEY]:
            return False
        if cherrypy.session[db.SESSION_KEY] in ['1208931', '1695846'] and groupname == 'admin': #Gurovic vkontakte login
            return True
        else:
            return False
    return check

def name_is(reqd_username):
    return lambda: reqd_username == cherrypy.session[db.SESSION_KEY]

# These might be handy

def any_of(*conditions):
    """Returns True if any of the conditions match"""
    def check():
        for c in conditions:
            if c():
                return True
        return False
    return check

# By default all conditions are required, but this might still be
# needed if you want to use it inside of an any_of(...) condition
def all_of(*conditions):
    """Returns True if all of the conditions match"""
    def check():
        for c in conditions:
            if not c():
                return False
        return True
    return check


# Controller to provide login and logout actions

class AuthController(object):
        
    @cherrypy.expose
    def view(self):
        if member_of('admin')():     
            result = '<b>Cписок пользователей</b><br><table border="1">'
            for row in db.session.query(User).all(): 
                result += '''<tr><td>{0}</td><td>{1}</td><td>{2}</td></tr>'''.format(row.username,
                          row.password, row.email)
            result += '</table>'
            return template(result)
        else:
            raise cherrypy.HTTPRedirect("/")            

    def get_login_form(self, msg="Вход на сайт", from_page="/"):
        return """
            <form method="post" action="/auth/login">
            <input type="hidden" name="from_page" value="{}" />
            {}<br />
            Логин: <input type="text" name="username" /><br />
            Пароль: <input type="password" name="password" /><br />
            <input type="submit" value="Войти" />
            </form>
            """.format(from_page, msg)
    
    def get_password_form(self, msg="Смена пароля"):
        return """
            <form method="get" action="/auth/change_password">
            <b>{}</b><br />
            Старый пароль: <input type="password" name="old_password" /><br />
            Новый пароль: <input type="password" name="new_password" /><br />
            Новый пароль (ещк раз): <input type="password" name="new_password2" /><br />
            <input type="submit" value="Сменить" />
            </form>
        """.format(msg)
    
    def get_registration_form(self):
        return """
            <form method="get" action="/authcontroller/register">
            Логин*: <input type="text" name="username" /><br />
            Пароль*: <input type="password" name="password" /><br />
            E-mail: <input type="text" name="email" /><br />
            <i>(для восстановления забытого пароля)</i><br>
            <input type="submit" value="Зарегистрироваться" />
            </form>
        """
    
    @cherrypy.expose
    def login(self, username=None, password=None, from_page="/",
              uid=None, hash=None, **args):
        app_id = '2458868' 
        secret_key = 'q6G5ajnhXZVQDBPU1Glh'
        if uid is not None:
        #vkontakte auth
            if hash != hashlib.md5((app_id + uid + secret_key).encode('utf-8')).hexdigest():
                return template(self.get_login_form('Vkontakte auth failed', from_page))
            else:
                db.session.add(User(uid, password, ''))
                db.session.add(Anketa(username=uid, year = date.today().year, vkontakte=uid))
                try:
                    db.session.commit()
                except sqlalchemy.exc.IntegrityError:
                    #user already exists
                    db.session.rollback()
                cherrypy.session[db.SESSION_KEY] = cherrypy.request.login = userlogin = uid
                raise cherrypy.HTTPRedirect((from_page or "/"))
        else:
            if username is None or password is None:
                return template(self.get_login_form("", from_page=from_page))
       
            error_msg = check_credentials(username, password)
            if error_msg:
                return template(self.get_login_form(error_msg, from_page))
            else:
                cherrypy.session[db.SESSION_KEY] = cherrypy.request.login = userlogin = username
                raise cherrypy.HTTPRedirect((from_page or "/"))
    
    @cherrypy.expose
    def register(self, username, password, email=None):
        print(username,password,email)
        db.session.add(User(username, password, email))
        db.session.add(Anketa(username=username, year = date.today().year, email=email))
        try:
            db.session.commit()
        except sqlalchemy.exc.IntegrityError:
            db.session.rollback()
            return template("Пользователь с таким логином уже существует.<br><br>" + self.get_registration_form())
        self.login(username=username, password=password, from_page='/')

    @cherrypy.expose
    def change_password(self,old_password=None,new_password=None,new_password2=None):
        if not old_password:
            try:
                int(cherrypy.session[db.SESSION_KEY])
                return template("Вы зашли, используя авторизацию сайта Вконтакте.")
            except:
                return template(self.get_password_form())
        else:
            u = db.session.query(User).get(cherrypy.session[db.SESSION_KEY])
            if u is None:
                raise cherrypy.HTTPRedirect("/")
            elif u.password != old_password:
                return template(self.get_password_form('Неверно указан старый пароль.'))
            elif new_password != new_password2:
                return template(self.get_password_form('Новый пароль не совпадает с повторно введенным.'))
            else:
                u.password = new_password
                db.session.commit()
                raise cherrypy.HTTPRedirect("/")

    @cherrypy.expose
    def logout(self, from_page="/"):
        sess = cherrypy.session
        username = sess.get(db.SESSION_KEY, None)
        sess[db.SESSION_KEY] = None
        if username:
            cherrypy.request.login = None
        raise cherrypy.HTTPRedirect(from_page or "/")

